﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;


//http://stackoverflow.com/questions/1312166/print-full-signature-of-a-method-from-a-methodinfo
    namespace System.Reflection
    {
        public static class MethodInfoExtensions
        {
            /// <summary>
            /// Return the method signature as a string.
            /// </summary>
            /// <param name="method">The Method</param>
            /// <param name="callable">Return as an callable string(public void a(string b) would return a(b))</param>
            /// <returns>Method signature</returns>
            public static string GetSignature(this MethodInfo method, bool callable = false)
            {
                var firstParam = true;
                var sigBuilder = new StringBuilder();
                if (callable == false)
                {
                    if (method.IsPublic)
                        sigBuilder.Append("public ");
                    else if (method.IsPrivate)
                        sigBuilder.Append("private ");
                    else if (method.IsAssembly)
                        sigBuilder.Append("internal ");
                    if (method.IsFamily)
                        sigBuilder.Append("protected ");
                    if (method.IsStatic)
                        sigBuilder.Append("static ");
                    sigBuilder.Append(TypeName(method.ReturnType));
                    sigBuilder.Append(' ');
                }
                sigBuilder.Append(method.Name);

                // Add method generics
                if (method.IsGenericMethod)
                {
                    sigBuilder.Append("<");
                    foreach (var g in method.GetGenericArguments())
                    {
                        if (firstParam)
                            firstParam = false;
                        else
                            sigBuilder.Append(", ");
                        sigBuilder.Append(TypeName(g));
                    }
                    sigBuilder.Append(">");
                }
                sigBuilder.Append("(");
                firstParam = true;
                var secondParam = false;
                foreach (var param in method.GetParameters())
                {
                    if (firstParam)
                    {
                        firstParam = false;
                        if (method.IsDefined(typeof(System.Runtime.CompilerServices.ExtensionAttribute), false))
                        {
                            if (callable)
                            {
                                secondParam = true;
                                continue;
                            }
                            sigBuilder.Append("this ");
                        }
                    }
                    else if (secondParam == true)
                        secondParam = false;
                    else
                        sigBuilder.Append(", ");
                    if (param.ParameterType.IsByRef)
                        sigBuilder.Append("ref ");
                    else if (param.IsOut)
                        sigBuilder.Append("out ");
                    if (!callable)
                    {
                        sigBuilder.Append(TypeName(param.ParameterType));
                        sigBuilder.Append(' ');
                    }
                    sigBuilder.Append(param.Name);
                }
                sigBuilder.Append(")");
                return sigBuilder.ToString();
            }

            /// <summary>
            /// Get full type name with full namespace names
            /// </summary>
            /// <param name="type">Type. May be generic or nullable</param>
            /// <returns>Full type name, fully qualified namespaces</returns>
            public static string TypeName(Type type)
            {
                var nullableType = Nullable.GetUnderlyingType(type);
                if (nullableType != null)
                    return nullableType.Name + "?";

                if (!type.IsGenericType)
                    switch (type.Name)
                    {
                        case "String": return "string";
                        case "Int32": return "int";
                        case "Decimal": return "decimal";
                        case "Object": return "object";
                        case "Void": return "void";
                        default:
                            {
                                return string.IsNullOrWhiteSpace(type.FullName) ? type.Name : type.FullName;
                            }
                    }

                var sb = new StringBuilder(type.Name.Substring(0,
                type.Name.IndexOf('`'))
                );
                sb.Append('<');
                var first = true;
                foreach (var t in type.GetGenericArguments())
                {
                    if (!first)
                        sb.Append(',');
                    sb.Append(TypeName(t));
                    first = false;
                }
                sb.Append('>');
                return sb.ToString();
            }

        }
    }

